package com.shaoyu.spring.study.runner;

import com.shaoyu.spring.study.annotation.HandlerExecutor;
import com.shaoyu.spring.study.common.CommonUtil;
import com.shaoyu.spring.study.dto.BaseRequest;
import com.shaoyu.spring.study.dto.BaseResponse;
import com.shaoyu.spring.study.handler.BasicHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;


/**
 * @Description:
 * @author: shaoyu1
 * @date: 2020/11/4 14:56:59
 */
@Slf4j
public class HandlerRunner implements ApplicationContextAware{

    private final String requestKey = "HandlerRunner_request";
    private final String responseKey = "HandlerRunner_response";
    private final String groupKey = "HandlerRunner_group";
    
    private HandlerContext handlers;
    private ApplicationContext applicationContext;

    private final ExecutorService executorService;

    private final ConcurrentWarp concurrentWarp;

    public HandlerRunner(ConcurrentWarp concurrentWarp, ExecutorService executorService) {
        this.concurrentWarp = concurrentWarp;
        this.executorService = executorService;
    }

    /**
     * 执行某个分组的处理器
     * @param group 业务分组
     */
    public void execute(String group, BaseRequest request, BaseResponse response) {
        handlers = HandlerContext.getCurrentContext();
        handlers.set(this.requestKey,request);
        handlers.set(this.responseKey,response);
        handlers.set(this.groupKey,group);
        handlers.initInterruptedFlag();
        init(group);
        try{
            runHandlers(CommonUtil.HANDLER_PRE);
            runHandlers(CommonUtil.HANDLER_CENTER);
            runHandlers(CommonUtil.HANDLER_POST);
        }catch (Throwable e){
            try{
                handlers.put("error",e);
                runHandlers(CommonUtil.HANDLER_ERROR);
            }catch (Throwable t){
                log.error("执行处理器出错:",t);
                throw new RuntimeException(t);
            }
        }finally {
            handlers.unset();
        }
    }
    /**
     * 初始化
     */
    private void init(String group){
        //缓存
        Map<String, BasicHandler> beans = applicationContext.getBeansOfType(BasicHandler.class);
        Map<Integer,List<BasicHandler>> preFilters = new HashMap<>();
        Map<Integer,List<BasicHandler>> postFilters = new HashMap<>();
        Map<Integer,List<BasicHandler>> routeFilters = new HashMap<>();
        Map<Integer,List<BasicHandler>> errorFilters = new HashMap<>();
        for (Object bean:beans.values()) {
            Class clz = getProxyRealClass(bean.getClass());
            HandlerExecutor attr = (HandlerExecutor)clz.getAnnotation(HandlerExecutor.class);
            if(attr!=null&&checkGroups(attr.groups(),group)){
                if(CommonUtil.HANDLER_PRE.equals(attr.type())){
                    putFilter(attr.order(),(BasicHandler)bean,preFilters);
                }else if (CommonUtil.HANDLER_POST.equals(attr.type())){
                    putFilter(attr.order(),(BasicHandler)bean,postFilters);
                }else if (CommonUtil.HANDLER_CENTER.equals(attr.type())){
                    putFilter(attr.order(),(BasicHandler)bean,routeFilters);
                }else if(CommonUtil.HANDLER_ERROR.equals(attr.type())){
                    putFilter(attr.order(),(BasicHandler)bean,errorFilters);
                }
            }
        }
        handlers.put(CommonUtil.HANDLER_PRE,preFilters);
        handlers.put(CommonUtil.HANDLER_POST,postFilters);
        handlers.put(CommonUtil.HANDLER_CENTER,routeFilters);
        handlers.put(CommonUtil.HANDLER_ERROR,errorFilters);
    }

    private Class getProxyRealClass(Class clz) {
        if(clz.equals(Object.class))return Object.class;
        if(clz.getSuperclass().equals(BasicHandler.class))return clz;
        return getProxyRealClass(clz.getSuperclass());
    }

    /**
     * 检查是不是当前业务线的处理器
     * @param groups
     * @param group
     * @return
     */
    private boolean checkGroups(String[] groups,String group) {
        if(groups!=null&&groups.length>0){
            for (String gp:groups){
                if(gp.equals(CommonUtil.HANDLER_DEFAULT))return true;
                if(gp.equals(group))return true;
            }
        }
        return false;
    }

    private void putFilter(int order, BasicHandler filter, Map<Integer, List<BasicHandler>> filters) {
        if(CollectionUtils.isNotEmpty(filters.get(order))){
            filters.get(order).add(filter);
        }else{
            List l = new ArrayList<>();
            l.add(filter);
            filters.put(order,l);
        }
    }

    private void runHandlers(String sType) throws Throwable {
        BaseRequest request = (BaseRequest)handlers.get(this.requestKey);
        BaseResponse response = (BaseResponse)handlers.get(this.responseKey);
        Map<Integer,List<BasicHandler>> maps = (Map<Integer,List<BasicHandler>>)this.handlers.get(sType);
        if (MapUtils.isNotEmpty(maps)) {
            List<Integer> o = new ArrayList<>(maps.keySet());
            Collections.sort(o);
            for (int i = 0; i < o.size(); i++) {
                List<BasicHandler> orderFilters = maps.get(o.get(i));
                if(CollectionUtils.isNotEmpty(orderFilters)){
                    if(handlers.isInterrupted())return;
                    int size = orderFilters.size();
                    if(size>1){
                        List<? extends Future<?>> futures = orderFilters.stream().map(filter ->
                                executorService.submit(concurrentWarp.warp(()-> filter.run(request, response, handlers)))
                        ).collect(Collectors.toList());
                        futures.stream().forEach(f-> {
                            try {
                                f.get();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        });
                    }else{
                        orderFilters.get(0).run(request,response,handlers);
                    }
                }
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
